﻿using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using CardPlay.Model.CardModel;
using CardPlay.Model.GameModel.Actions;

namespace CardPlay.Model.GameModel
{
    public abstract class StandardCardGame : CardGame
    {
        private readonly List<string> _namesOfAdditionalPiles = new List<string>();

        #region Standard part names

        public const string Foundation1 = StandardPileNames.PrefixForFoundation + "1";
        public const string Foundation2 = StandardPileNames.PrefixForFoundation + "2";
        public const string Foundation3 = StandardPileNames.PrefixForFoundation + "3";
        public const string Foundation4 = StandardPileNames.PrefixForFoundation + "4";
        public const string Foundation5 = StandardPileNames.PrefixForFoundation + "5";
        public const string Foundation6 = StandardPileNames.PrefixForFoundation + "6";
        public const string Foundation7 = StandardPileNames.PrefixForFoundation + "7";
        public const string Foundation8 = StandardPileNames.PrefixForFoundation + "8";
        public const string Tableau1 = StandardPileNames.PrefixForTableau + "1";
        public const string Tableau2 = StandardPileNames.PrefixForTableau + "2";
        public const string Tableau3 = StandardPileNames.PrefixForTableau + "3";
        public const string Tableau4 = StandardPileNames.PrefixForTableau + "4";
        public const string Tableau5 = StandardPileNames.PrefixForTableau + "5";
        public const string Tableau6 = StandardPileNames.PrefixForTableau + "6";
        public const string Tableau7 = StandardPileNames.PrefixForTableau + "7";
        public const string Tableau8 = StandardPileNames.PrefixForTableau + "8";

        #endregion

        protected StandardCardGame()
        {
            NamesOfAdditionalPiles.AddRange(new[]{ Stock, Waste });

            TotalDecks = 1;
            TotalFoundations = CardFace.SuitsPerDeck;
            TotalTableaus = 4;
            FoundationBaseRank = Rank.Ace;

            FoundationBuildSequence = CardFaceSequence.SameSuitAscendingRank;
            TableauBuildSequence = CardFaceSequence.SameSuitDescendingRank;

            InitialDealCompleted += delegate
                                    {
                                        var dealtFoundation = this.NonEmptyPilesNamed(AnyFoundation).FirstOrDefault();
                                        if (dealtFoundation != null) FoundationBaseRank = dealtFoundation.BottomCard.Rank;
                                    };
        }

        protected List<string> NamesOfAdditionalPiles { get { return _namesOfAdditionalPiles; } }
        protected int TotalDecks { get; set; }
        protected int TotalTableaus { get; set; }
        protected int TotalFoundations { get; set; }
        protected Rank FoundationBaseRank { get; set; }

        protected IEnumerable<string> NamesOfFoundations 
        {
            get { return StandardPileNames.Foundations.Take(TotalFoundations); }
        }

        protected IEnumerable<string> NamesOfTableaus
        {
            get { return StandardPileNames.Tableaus.Take(TotalTableaus); }
        }

        protected CardFaceSequenceSpecification FoundationBuildSequence { get; set; }
        protected CardFaceSequenceSpecification TableauBuildSequence { get; set; }

        protected override IEnumerable<string> InitialPileNames
        {
            get { return NamesOfFoundations.Concat(NamesOfTableaus).Concat(NamesOfAdditionalPiles); }
        }

        protected override IEnumerable<Card> CardsInInitialDeal
        {
            get
            {
#if DEBUG
                var attribute = GetType().GetCustomAttributes(typeof (GameAttribute), false).Cast<GameAttribute>().FirstOrDefault();
                Debug.Assert(attribute != null);
                Debug.Assert(attribute.NumberOfDecks == TotalDecks);
#endif
                return DeckFactory.CombineIdenticalDecks(TotalDecks);
            }
        }

        protected override IDealAction CreateDealScript()
        {
            var script = new DealScript();

            PrepareDealScript(script);
            return script.Compile();
        }

        protected abstract void PrepareDealScript(DealScript script);

        protected override IPlayingRules CreateRegulations()
        {
            var playingRules = new PlayingRules();

            playingRules.At(AnyFoundation)
                .Permit<MovePileAction>().When(AllowedToBuildFoundation);

            playingRules.At(AnyReserve)
                .Permit<FlipCardAction>().When(Action.IsActingOnFaceDownTopCard);

            playingRules.At(AnyTableau)
                .ConditionallyPermit<FlipCardAction>(Action.IsActingOnFaceDownTopCard)
                .ConditionallyPermit<RemovePileAction>(AllowedToPickupAtTableau)
                .ConditionallyPermit<MovePileAction>(action => action.TargetPile.IsEmpty ? AllowedToBuildEmptyTableau(action) : AllowedToBuildNonEmptyTableau(action));

            playingRules.At(Waste)
                .ConditionallyPermit<RemovePileAction>(Action.IsActingOnFaceUpTopCard)
                .ConditionallyPermit<ResetStockAction>(action => action.CardGame.IsRedealPossible && action.IsActingOnNonEmptyPile() && action.TargetPile.IsEmpty);

            playingRules.At(Stock)
                .Permit<DealAction>().When(Action.IsActingOnFaceDownTopCard);

            playingRules.Permit<RemovePileAction>().When(AllowedToPickupAtNonTableau);
            playingRules.Permit<PeekCardAction>().When(action => action.IsActingOnFaceUpCard() && !action.IsActingOnTopCard());
            //playingRules.Permit<MovePileAction>().When(AllowedToBuildOther);

            OverridePlayingRules(playingRules);
            return playingRules;
        }

        protected virtual void OverridePlayingRules(PlayingRules playingRules)
        {
        }

        protected virtual bool AllowedToPickupAtNonTableau(RemovePileAction action)
        {
            return false;
        }

        protected virtual bool AllowedToPickupAtTableau(RemovePileAction action)
        {
            return action.IsActingOnFaceUpTopCard() || action.IsActingOnEntirePile();
        }

        protected virtual bool AllowedToBuildFoundation(MovePileAction action)
        {
            return action.IsBuildingFoundation(FoundationBuildSequence, FoundationBaseRank);
        }

        protected virtual bool AllowedToBuildEmptyTableau(MovePileAction action)
        {
            return action.PileBeingMoved.BottomCard.Rank != FoundationBaseRank;
        }

        protected virtual bool AllowedToBuildNonEmptyTableau(MovePileAction action)
        {
            return action.PileBeingMoved.BottomCard.Rank != FoundationBaseRank && action.IsBuilding(TableauBuildSequence);
        }

        //protected virtual bool AllowedToBuildOther(MovePileAction action)
        //{
        //    return false;
        //}
    }

}